//放置上传图片的处理函数
//导入处理文件上传的包
const formidable = require("formidable");
const multiparty = require("multiparty");
const path = require("path");
const fs = require("fs");
const db = require("../../mysql/db");
exports.imageUp = (req, res, next) => {
  const form = formidable({
    multiples: true,
    uploadDir: path.join(__dirname, "../../public/images"),
    keepExtensions: true,
  });
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    console.log(files);
    //切割出上传的文件的后缀名
    let ext = files.file.mimetype.split("/")[1];
    //计算出图片文件大小
    let size = (files.file.size / 1024 / 1024).toFixed(2);
    if (
      (ext == "png" || ext == "jpg" || ext == "jepg" || ext == "mp4") &&
      size < 2
    ) {
      let url = "http://127.0.0.1:3000/images/" + files.file.newfileName;
      res.send({
        code: 200,
        msg: "上传成功",
        imgUrl: url,
      });
    } else {
      res.send({
        code: 400,
        msg: "只能上传png、jpg、jpeg格式的图片或图片过大",
      });
      return;
    }
  });
};

exports.fileUp = (req, res, next) => {
  //上传大小小于1Mb的文件
  //接收数据
  const form = formidable({
    multiples: true,
    uploadDir: path.join(__dirname, "../../public/file"),
    keepExtensions: true,
  });
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    //限制上传文件的大小
    if (files.file.size > 1024 * 1024 * 100000) {
      //删除对应的文件
      const folderPath = path.join(
        __dirname,
        "../../public/file/" + files.file.newfileName
      ); // 文件路径
      fs.unlinkSync(folderPath);
      res.send({
        code: 400,
        msg: "上传文件过大",
      });
      return;
    }
    //修改保存文件的默认name
    const folderPath = path.join(
      __dirname,
      "../../public/file/" + files.file.newfileName
    ); // 文件路径
    let newName = path.join(
      __dirname,
      "../../public/file/" + files.file.originalfileName
    );

    //对读取的文件进行重命名
    fs.rename(folderPath, newName, (err) => {
      if (err) {
        console.log(err);
        return;
      } else {
        console.log("重命名成功");
        res.send({
          code: 200,
          msg: "上传成功",
        });
      }
    });
  });
};
// 上传至数据库
exports.upload_chunk = (req, res, next) => {
  // 二进制数据上传
  const form = new multiparty.Form();

  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    if (parseInt(fields.index[0]) === 0) {
      //查看数据库中是否存在此hash
      const sql = "SELECT * FROM hash WHERE hash = ?";
      db.query(sql, [fields["fileName"][0]], (err, result) => {
        if (err) {
          next(err);
          return;
        }
        if (result.length > 0) {
          //如果存在则直接返回
          res.send({
            code: 300,
            msg: "已经存在部分文件",
            index: result[0].dex + 1,
          });
          return;
        } else {
          //向数据库中插入一个hash
          console.log(11111);
          const sql = "INSERT INTO hash (hash, dex) VALUES (?, ?)";
          db.query(sql, [fields["fileName"][0], 0], (err, result) => {
            if (err) {
              next(err);
              return;
            }
            console.log(result);
            res.send({
              code: 300,
              msg: "已经存在部分文件",
              index: 0,
            });
            return;
          });
        }
      });
    } else {
      //将每一次上传的数据进行统一的存储
      const oldName = files.chunk[0].path;
      const newName = path.join(
        __dirname,
        "../../public/upload/chunk/" +
          fields["fileName"][0] +
          "/" +
          fields["name"][0]
      );

      //创建临时存储目录
      fs.mkdirSync("./public/upload/chunk/" + fields["fileName"][0], {
        recursive: true,
      });
      console.log(fields);
      console.log(files);
      // fs.rename(oldName, newName, (err) => {
      //   if (err) {
      //     console.log(err)
      //     return
      //   } else {
      //     console.log('重命名成功')
      //     res.send({
      //       code: 200,
      //       msg: '上传成功'
      //     })
      //   }
      // })
      // 这个错误是由于跨设备的文件移动操作引发的，而在大多数操作系统中，使用fs.rename函数进行跨设备的文件移动是不被允许的。这是因为rename是原子操作，而跨设备移动需要复制文件内容到目标设备，然后删除源文件，这不是一个原子操作。
      // 复制文件
      fs.copyFile(oldName, newName, (err) => {
        if (err) {
          console.error(err);
        } else {
          // 删除源文件
          fs.unlink(oldName, (err) => {
            if (err) {
              console.error(err);
            } else {
              console.log("文件复制和删除成功");
            }
          });
        }
      });
      //修改hash值的dex
      const sql = "update hash set dex = ? where hash = ?";
      db.query(
        sql,
        [parseInt(fields.index[0]), fields.fileName[0]],
        (err, result) => {
          if (err) {
            console.log(err);
          } else {
            console.log(result);
          }
        }
      );
      res.send({
        code: 200,
        msg: "分片上传成功",
      });
    }
  });
};

// 所有上传的文件存放到uploads目录下
const UPLOAD_DIR = path.join(__dirname, "../../public/upload");

// const writePath =

exports.upload_chunk1 = async (req, res, next) => {
  // 二进制数据上传
  const form = new multiparty.Form();
  form.parse(req, async (err, fields, files) => {


    setTimeout(() => {
      res.status(200).json({
        code: 200,
        msg: "分片上传成功",
        data: {
          shouldUpload: true,
          // path: chunkWritePath,
        },
      });
    },3000)

      return

    if (err) {
      res.status(401).json({
        ok: false,
        msg: "上传失败",
      });
    }
    if (!fields?.chunkHash[0]) return console.log("缺少必要参数,请求已取消");

    const chunkHash = fields["chunkHash"][0]; // 当前切片的名称(也就是 哈希值-索引值)
    const chunkNumber = fields.chunkNumber[0]; // 当前切片的索引
    const fileName = fields["fileName"][0]; // 大文件的名称
    const fileHash = fields["fileHash"][0]; // 整个文件的hash值
    const totalChunks = fields["totalChunks"][0]; // 分片个数

    // 存储切片的临时文件夹，命名规则：存储路径/哈希值-分片个数
    const chunkDir = `${UPLOAD_DIR}/${fileHash}#${totalChunks}/chunk`;
    // 文件的源路径
    const oldPath = files.chunkData[0].path;
    // 分片写入路径，命名规则：存储路径/索引值@哈希值
    const chunkWritePath = `${chunkDir}/${chunkNumber}@${chunkHash}`;

    // 断点续传（对待上传文件是否存在进行处理）
    if (fs.existsSync(chunkWritePath)) {
      // 文件存在服务器中，不需要再上传了
      return res.status(200).json({
        code: 200,
        msg: "文件已存在",
        data: {
          shouldUpload: false,
          path: chunkWritePath,
        },
      });
    }
    // 需要上传
    else {
      // 切片目录不存在，则创建切片目录
      if (!fs.existsSync(chunkDir)) {
        // 异步创建文件夹
        await fs.promises.mkdir(chunkDir, { recursive: true }, (error) => {
          if (error) {
            console.log(error);
          } else {
            console.log("目录创建成功");
          }
        });
      }

      // 把文件切片移动到我们的切片文件夹中
      try {
        await fs.promises.access(oldPath); // 检查源文件是否存在
        console.log("源文件存在：", oldPath);
        await fs.promises.access(chunkDir); // 检查目标目录是否存在
        console.log("目标目录存在：", chunkDir);

        // 复制文件
        await fs.promises.copyFile(oldPath, chunkWritePath);
        console.log("分片缓存成功：", chunkWritePath);

        // 删除原文件
        await fs.promises.unlink(oldPath);
        console.log("原文件删除成功：", oldPath);
      } catch (error) {
        console.log("移动文件失败：", error);
      }

      // // 上传成功
      res.status(200).json({
        code: 200,
        msg: "分片上传成功",
        data: {
          shouldUpload: true,
          path: chunkWritePath,
        },
      });
      console.log("大文件切片上传成功");
    }
  });
};
// 合并文件
exports.merge_chunk = async (req, res, next) => {

  setTimeout(() => {
    res.status(200).json({
      code: 200,
      msg: "合并成功",
    });
  }, 3000);
  return
  // 提取文件后缀名
  const extractExt = (filename) => {
    return filename.slice(filename.lastIndexOf("."), filename.length);
  };

  console.log("请求体信息", req.body);
  const { size, totalChunks, fileHash, fileName } = req.body;

  // 找到待合并文件的切片文件夹的位置
  const filePath = path.resolve(
    UPLOAD_DIR,
    // `${fileHash}/${extractExt(fileName)}`
    `${fileHash}#${totalChunks || "null"}/${fileName}`
  );
  // 如果已合并的大文件已经存在，则直接返回
  if (fs.existsSync(filePath)) {
    res.status(200).json({
      code: 200,
      msg: "合并成功",
    });
    console.log("大文件已合并");
    return;
  }

  // // 文件的源路径
  // const oldPath = files.chunkData[0].path
  // // 分片写入路径
  // const chunkWritePath = `${chunkDir}/${chunkNumber}@${chunkHash}`
  // 如果已合并的大文件不存在，则找到它的切片的文件夹
  const chunkDir = `${UPLOAD_DIR}/${fileHash}#${totalChunks || "null"}/chunk`;
  // 切片目录不存在，则无法合并切片，报异常
  if (!fs.existsSync(chunkDir)) {
    res.status(200).json({
      code: 1,
      msg: "合并失败，请重新上传",
    });
    console.log("切片目录不存在");
    return;
  }
  await mergeFileChunk(filePath, fileHash, size, chunkDir);
  res.status(200).json({
    code: 200,
    msg: "合并成功",
  });
};
// 验证分片上传
exports.verify_chunk = async (req, res, next) => {
  const { fileHash, fileName, chunkSize, totalChunks } = req.body;

  // 读取目录内容
  const files = fs.readdirSync(UPLOAD_DIR);

  const vName = fileHash + "#" + totalChunks;

  if (!files.includes(vName)) {
    return res.status(200).json({
      code: 200,
      msg: "没有该文件，请上传",
      data: {
        shouldUpload: false,
        isComplete: false,
        existList: [],
      },
    });
  }
  // 存储文件夹名称
  // const folderNames = [];

  // // 遍历目录项
  // for (const file of files) {
  //   debugger
  //   const name = file + '-' + totalChunks
  //   const fullPath = path.join(dirPath, name);
  //   const stats = fs.statSync(fullPath);

  //   // 判断是否为文件夹
  //   if (stats.isDirectory()) {
  //     folderNames.push(file);
  //   }
  // }
  // console.log('同一级文件夹名称:', folderNames);
  if (files.includes(vName)) {
    //
    const videoDir = `${UPLOAD_DIR}/${vName}`;
    if (hasVideoFiles(videoDir)) {
      return res.status(200).json({
        code: 200,
        msg: "已有此文件",
        data: {
          shouldUpload: false,
          isComplete: true,
          // 已上传的分片id
          existList: Array.from({ length: totalChunks  }, (_, i) => i + 1),
        },
      });
    } else {
      const chunkDir = `${UPLOAD_DIR}/${fileHash}/chunk`;
      
      return res.status(200).json({
      code: 200,
      msg: "没有该文件，请上传",
      data: {
        shouldUpload: false,
        isComplete: false,
        existList: [],
      },
    });
    }
  }
};

/**
 * 合并文件夹中的切片，生成一个完整的文件
 */
async function mergeFileChunk(filePath, fileHash, size, chunkDir) {
  // 找到该文件的切片存放的文件夹的路径
  // const chunkDir = path.resolve(UPLOAD_DIR, fileHash)
  // 读取目录下的所有子目录，也就是读取该文件的所有切片
  const chunkPaths = await fs.promises.readdir(chunkDir);
  // 根据切片下标进行排序
  // 否则直接读取目录的获得的顺序可能会错乱
  chunkPaths.sort((a, b) => {
    return a.split("@")[0] - b.split("@")[0];
  });

  const list = chunkPaths.map((chunkPath, index) => {
    return pipeStream(
      path.resolve(chunkDir, chunkPath),
      fs.createWriteStream(filePath, {
        start: index * size,
        end: (index + 1) * size,
      })
    );
  });

  await Promise.all(list);
  // 文件合并后删除保存切片的目录
  fs.rmdirSync(chunkDir);
}
/**
 * 读的内容写到writeStream中
 */
const pipeStream = (path, writeStream) => {
  return new Promise((resolve, reject) => {
    // 创建可读流
    const readStream = fs.createReadStream(path);
    readStream.on("end", async () => {
      // 删除源文件
      fs.unlinkSync(path);
      resolve();
    });
    // 将可读流连接到可写流
    readStream.pipe(writeStream);
  });
};

/**
 * 判断文件夹下是否包含视频文件
 * @param {string} folderPath - 文件夹路径
 * @returns {boolean} - 是否包含视频文件
 */
function hasVideoFiles(folderPath) {
  try {
    // 1. 读取文件夹内容
    const files = fs.readdirSync(folderPath);

    // 2. 支持的视频文件扩展名列表（小写）
    const videoExtensions = [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv"];

    // 3. 遍历文件
    for (const file of files) {
      const fullPath = path.join(folderPath, file);
      const stats = fs.statSync(fullPath);

      // 4. 仅检查文件（排除子文件夹）
      if (stats.isFile()) {
        const ext = path.extname(file).toLowerCase(); // 转换为小写避免大小写问题

        // 5. 判断是否为视频文件
        if (videoExtensions.includes(ext)) {
          return true; // 存在视频文件
        }
      }
    }

    return false; // 未找到视频文件
  } catch (err) {
    console.error(`读取文件夹失败: ${err.message}`);
    return false;
  }
}
